﻿using System;
using System.ComponentModel;

namespace Microsoft.Xna.Framework
{
    /// <summary>
    /// Represents a point in a multi-point curve.
    /// </summary>
    [Serializable]
    public class CurveKey : IEquatable<CurveKey>, IComparable<CurveKey>
    {
        #region Private Fields
        private CurveContinuity continuity;
        private float position;
        private float tangentIn;
        private float tangentOut;
        private float value;
        #endregion

        #region Properties
        /// <summary>
        /// Describes whether the segment between this point and the next point in the curve is discrete or continuous.
        /// </summary>
        public CurveContinuity Continuity
        {
            get { return this.continuity; }
            set { this.continuity = value; }
        }
        /// <summary>
        /// Position of the CurveKey in the curve.
        /// </summary>
        public float Position
        {
            get { return this.position; }
        }
        /// <summary>
        /// Describes the tangent when approaching this point from the previous point in the curve.
        /// </summary>
        public float TangentIn
        {
            get { return this.tangentIn; }
            set { this.tangentIn = value; }
        }
        /// <summary>
        /// Describes the tangent when leaving this point to the next point in the curve.
        /// </summary>
        public float TangentOut
        {
            get { return this.tangentOut; }
            set { this.tangentOut = value; }
        }
        /// <summary>
        /// Describes the value of this point.
        /// </summary>
        public float Value
        {
            get { return this.value; }
            set { this.value = value; }
        }
        #endregion

        #region Constructor
        /// <summary>
        /// Initializes a new instance of CurveKey.
        /// </summary>
        /// <param name="position">Position in the curve.</param>
        /// <param name="value">Value of the control point.</param>
        public CurveKey(float position, float value)
            : this(position, value, 0, 0, CurveContinuity.Smooth)
        {

        }
        /// <summary>
        /// Initializes a new instance of CurveKey.
        /// </summary>
        /// <param name="position">Position in the curve.</param>
        /// <param name="value">Value of the control point.</param>
        /// <param name="tangentIn">Tangent approaching point from the previous point in the curve.</param>
        /// <param name="tangentOut">Tangent leaving point toward next point in the curve.</param>
        public CurveKey(float position, float value, float tangentIn, float tangentOut)
            : this(position, value, tangentIn, tangentOut, CurveContinuity.Smooth)
        {

        }
        /// <summary>
        /// Initializes a new instance of CurveKey.
        /// </summary>
        /// <param name="position">Position in the curve.</param>
        /// <param name="value">Value of the control point.</param>
        /// <param name="tangentIn">Tangent approaching point from the previous point in the curve.</param>
        /// <param name="tangentOut">Tangent leaving point toward next point in the curve.</param>
        /// <param name="continuity">Enum indicating whether the curve is discrete or continuous.</param>
        public CurveKey(float position, float value, float tangentIn, float tangentOut, CurveContinuity continuity)
        {
            this.position = position;
            this.value = value;
            this.tangentIn = tangentIn;
            this.tangentOut = tangentOut;
            this.continuity = continuity;
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Creates a copy of the CurveKey.
        /// </summary>
        public CurveKey Clone()
        {
            return new CurveKey(this.position, this.value, this.tangentIn, this.tangentOut, this.continuity);
        }
        /// <summary>
        /// Compares this instance to another CurveKey and returns an indication of their relative values.
        /// </summary>
        /// <param name="other">CurveKey to compare to.</param>
        public int CompareTo(CurveKey other)
        {
            return this.position.CompareTo(other.position);
        }
        /// <summary>
        /// Returns a value that indicates whether the current instance is equal to a specified object.
        /// </summary>
        /// <param name="obj">Object with which to make the comparison.</param>
        public override bool Equals(object obj)
        {
            return (obj is CurveKey) ? ((CurveKey)obj) == this : false;
        }
        /// <summary>
        /// Determines whether the specified Object is equal to the CurveKey.
        /// </summary>
        /// <param name="other">The Object to compare with the current CurveKey.</param>
        public bool Equals(CurveKey other)
        {
            return (this == other);
        }
        /// <summary>
        /// Returns the hash code for this instance.
        /// </summary>
        public override int GetHashCode()
        {
            return this.position.GetHashCode() ^ this.value.GetHashCode() ^ this.tangentIn.GetHashCode() ^
                this.tangentOut.GetHashCode() ^ this.continuity.GetHashCode();
        } 
        #endregion

        #region Operators
        /// <summary>
        /// Determines whether two CurveKey instances are not equal.
        /// </summary>
        /// <param name="a">CurveKey on the left of the equal sign.</param>
        /// <param name="b">CurveKey on the right of the equal sign.</param>
        public static bool operator !=(CurveKey a, CurveKey b)
        {
            return !(a == b);
        }
        /// <summary>
        /// Determines whether two CurveKey instances are equal.
        /// </summary>
        /// <param name="a">CurveKey on the left of the equal sign.</param>
        /// <param name="b">CurveKey on the right of the equal sign.</param>
        public static bool operator ==(CurveKey a, CurveKey b)
        {
            if (object.Equals(a, null))
                return object.Equals(b, null);

            if (object.Equals(b, null))
                return object.Equals(a, null);

            return (a.position == b.position)
                && (a.value == b.value)
                && (a.tangentIn == b.tangentIn)
                && (a.tangentOut == b.tangentOut)
                && (a.continuity == b.continuity);
        }
        #endregion
    }
}
